package com.fenzhimedia.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Yi Dai 484201132@qq.com
 * @since 2022/3/7 15:47
 */

@Slf4j
@Component
@ServerEndpoint("/videoCallServerEndpoint")
public class VideoCallServerEndpoint {

    private static Map<String, Session> peerIdSessionMap;

    private static ObjectMapper objectMapper;

    static {
        VideoCallServerEndpoint.peerIdSessionMap = new ConcurrentHashMap<>();
        VideoCallServerEndpoint.objectMapper = new ObjectMapper();
    }


    @OnMessage
    public void onMessage(Session session, String stringMessage) {
        log.info("onMessage:the session is:{},the stringMessage is:{}",session,stringMessage);
        try {
            WebSocketMessage webSocketMessage = VideoCallServerEndpoint.objectMapper.readValue(stringMessage, WebSocketMessage.class);
            if (WebSocketMessage.TypeEnumeration.REGISTER_PEER.equals(webSocketMessage.getType())) {
                VideoCallServerEndpoint.peerIdSessionMap.put(String.valueOf(webSocketMessage.getContent()), session);
            } else if (WebSocketMessage.TypeEnumeration.LOG_OFF_PEER.equals(webSocketMessage.getType())) {
                VideoCallServerEndpoint.peerIdSessionMap.remove(String.valueOf(webSocketMessage.getContent()));
            }

            WebSocketMessage answerWebSocketMessage = new WebSocketMessage(WebSocketMessage.TypeEnumeration.REFRESH_PEER_IDS, VideoCallServerEndpoint.peerIdSessionMap.keySet());
            //由于writeValueAsString方法需要捕捉异常，反而不够简洁，所以不使用lambda表达式
            for (Map.Entry<String, Session> entry : VideoCallServerEndpoint.peerIdSessionMap.entrySet()) {
                entry.getValue().getAsyncRemote().sendText(VideoCallServerEndpoint.objectMapper.writeValueAsString(answerWebSocketMessage));
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    @OnClose
    public void onClose(Session session) {
        log.info("onClose:the session is:{}",session);

        String peerId = null;
        for (Map.Entry<String, Session> entry : VideoCallServerEndpoint.peerIdSessionMap.entrySet()) {
            if (entry.getValue().equals(session) || entry.getValue().getId().equals(session.getId())) {
                peerId = entry.getKey();
                break;
            }
        }
        if (Objects.nonNull(peerId)) {
            VideoCallServerEndpoint.peerIdSessionMap.remove(peerId);
        }
    }

    @OnError
    public void onError(Session session, Throwable e) {
        log.info("onError:the session is:{},e:{}",session,e);

        onClose(session);
        e.printStackTrace();
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    private static class WebSocketMessage {

        private Integer type;

        private Object content;

        private static class TypeEnumeration {

            public static final Integer REGISTER_PEER = 1;

            public static final Integer LOG_OFF_PEER = 2;

            public static final Integer REFRESH_PEER_IDS = 3;

        }
    }

}

